﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Galaktika.BI.Silverlight.Services;

namespace Galaktika.BI.Silverlight.ClientApp.Web.Commands
{
    public class QueryManager
    {
        private QueryManager()
        {
        }

        private const string COMMAND_MANAGER_HOLDER = "CommandManangerHolder";
        public static QueryManager Current
        {
            get
            {
                if (HttpContext.Current != null &&
                    HttpContext.Current.Session != null)
                {
                    var mgr = HttpContext.Current.Session[COMMAND_MANAGER_HOLDER] as QueryManager;
                    if (mgr == null)
                    {
                        mgr = new QueryManager();
                        HttpContext.Current.Session[COMMAND_MANAGER_HOLDER] = mgr;
                    }

                    return mgr;
                }

                return null;
            }
        }

        private class QueryDescriptor
        {
            public QueryDescriptor(
                string id,
                Func<InvokeResultSchema> query, 
                Func<bool> isCancelable,
                Func<bool> doCancel)
            {
                this.Id = id;
                this.Query = query;
                this.IsCancelable = isCancelable;
                this.DoCancel = doCancel;
            }

            public readonly string Id;
            public readonly Func<InvokeResultSchema> Query;
            public readonly Func<bool> DoCancel;
            public readonly Func<bool> IsCancelable;
            public QueryExecutionState State = QueryExecutionState.Queued;
        }

        private readonly Dictionary<string, QueryDescriptor> m_Queries = new Dictionary<string, QueryDescriptor>();
        public InvokeResultSchema ExecuteQuery(
            string id,
            Func<InvokeResultSchema> query,
            Func<bool> isCancelable)
        {
            return this.ExecuteQuery(id, query, isCancelable, null);
        }

        public InvokeResultSchema ExecuteQuery(
            string id,
            Func<InvokeResultSchema> query,
            Func<bool> isCancelable,
            Func<bool> doCancel)
        {
            var desc = new QueryDescriptor(id, query, isCancelable, doCancel);
            if (!string.IsNullOrEmpty(id))
            {
                lock (m_Queries)
                {
                    m_Queries.Add(id, desc);
                }
            }


            InvokeResultSchema res = null;
            try
            {
                desc.State = QueryExecutionState.Executing;
                res = desc.Query();
                desc.State = QueryExecutionState.Done;
            }
            catch
            {
                desc.State = QueryExecutionState.Canceled;
                throw;
            }

            return res;
        }

        public bool TryCancel(string id)
        {
            QueryDescriptor desc;
            lock (m_Queries)
            {
                m_Queries.TryGetValue(id, out desc);
            }

            if (desc != null && desc.IsCancelable())
            {
                desc.DoCancel();
            }

            return false;
        }

        public IEnumerable<QueryStateDescriptor> ExtractQueryDescriptors()
        {
            var res = new List<QueryStateDescriptor>();
            List<QueryDescriptor> queriesCopy;
            lock (m_Queries)
            {
                queriesCopy = new List<QueryDescriptor>(m_Queries.Values);
            }

            foreach (var desc in queriesCopy)
            {
                res.Add(new QueryStateDescriptor(desc.Id, desc.State));

                if (desc.State == QueryExecutionState.Done ||
                    desc.State == QueryExecutionState.Canceled)
                {
                    lock (m_Queries)
                    {
                        m_Queries.Remove(desc.Id);
                    }
                }
            }

            return res;
        }
    }
}
